home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac-Source 1994 July
/
Mac-Source_July_1994.iso
/
C and C++
/
Libraries
/
TurboTCP 1.0.1
/
TurboTCP.source
/
CTCPAsyncCall.cp
< prev
next >
Wrap
Text File
|
1993-12-10
|
9KB
|
343 lines
/*
** CTCPAsyncCall.cp
**
** TurboTCP support library
** TCP asynchronous call class
**
** Copyright © 1993, FrostByte Design / Eric Scouten
**
*/
#include "CTCPAsyncCall.h"
#include "CTCPDriver.h"
#include "CTCPStream.h"
// —— initiate TCP calls ——
/*______________________________________________________________________
**
** ITCPAsyncCall
**
** Create a new TCP asynchronous call object. This object scheme, albeit complex, allows
** calls to be truly asynchronous. Control is returned immediately to the caller, which goes
** about other business until completion has occurred.
**
** theStream (CTCPStream *): the stream which requested the call
**
*/
void CTCPAsyncCall::ITCPAsyncCall (CTCPStream *theStream)
{
itsStream = theStream;
qEntry.qSelfLink = this;
bypassTarget = NULL;
bypassProc = NULL;
MoveHHi((Handle) this); // since we pass a pointer to the call block,
this->Lock(TRUE); // the object can’t move
}
/*______________________________________________________________________
**
** DoAsyncCall
**
** Fills in the standard parameters for a TCP Device Manager call and executes the call.
**
** theCsCode (short): TCP operation code
** theParamBlockPtr (TCPiopb *): parameter block for TCP call
**
** return (OSErr): result code
**
*/
OSErr CTCPAsyncCall::DoAsyncCall (short theCsCode, TCPiopb *theParamBlockPtr)
{
BlockMove(theParamBlockPtr, &itsParamBlock, sizeof (TCPiopb));
itsParamBlock.ioCompletion = (TCPIOCompletionProc) &CompletionProc;
itsParamBlock.ioCRefNum = gTCPDriver->GetTCPRefNum();
itsParamBlock.csCode = theCsCode;
PBControlAsync((ParmBlkPtr) &itsParamBlock);
return (itsParamBlock.ioResult);
}
// —— process TCP call completion ——
/*______________________________________________________________________
**
** ProcessCompletion
**
** Respond to completion of this TCP call and all calls following it in the completion loop.
** After running through this loop, disposes of the call object, UNLESS it was a successful
** auto-receive call.
**
*/
void CTCPAsyncCall::ProcessCompletion (void)
{
Boolean disposeWhenDone;
// dispatch to general completion routine or to optimized routine for no-copy-receive calls
TRY {
if (itsParamBlock.csCode == TCPNoCopyRcv)
disposeWhenDone = DispatchNoCopyRcv();
else
disposeWhenDone = Dispatch();
if (disposeWhenDone) {
itsStream->ProcessAsyncCompletion(this);
Dispose();
}
} CATCH {
itsStream->ProcessAsyncCompletion(this);
Dispose();
} ENDTRY;
}
/*______________________________________________________________________
**
** Dispatch (protected method)
**
** Called by ProcessCompletion to report back to the CTCPStream object. Does not dispose
** of object. Also, does not handle TCPNoCopyRcv commands (see DispatchNoCopyRcv
** method below).
**
** return (Boolean): TRUE to dispose of call object when done
**
*/
Boolean CTCPAsyncCall::Dispatch (void)
{
rdsEntry *RDSPtr;
wdsEntry *WDSPtr;
if (itsParamBlock.ioResult) {
// command failed, what was it?
switch (itsParamBlock.csCode) {
case TCPPassiveOpen:
case TCPActiveOpen:
itsStream->HandleOpenFailed(itsParamBlock.ioResult);
break;
case TCPSend:
itsStream->HandleSendFailed((wdsEntry *) itsParamBlock.csParam.send.wdsPtr,
itsParamBlock.ioResult);
break;
case TCPRcv:
if (itsParamBlock.ioResult == connectionClosing)
break;
case TCPClose:
if (itsParamBlock.ioResult == connectionDoesntExist)
break;
default:
itsStream->HandleTCPError(itsParamBlock.ioResult, itsParamBlock.csCode);
break;
}
}
else {
// no error, dispatch success — what type of call was this anyway?
switch (itsParamBlock.csCode) {
case TCPPassiveOpen:
case TCPActiveOpen:
itsStream->HandleOpened();
break;
case TCPSend:
WDSPtr = (wdsEntry *) itsParamBlock.csParam.send.wdsPtr;
itsStream->HandleDataSent(WDSPtr);
if (itsParamBlock.csParam.open.security) {
while ((*WDSPtr).length) {
ForgetPtr((*WDSPtr).ptr);
WDSPtr++;
}
}
ForgetPtr(itsParamBlock.csParam.send.wdsPtr);
break;
case TCPRcv:
if (itsParamBlock.csParam.receive.urgentFlag)
itsStream->RcvUrgentBegin();
if (itsParamBlock.csParam.receive.markFlag)
itsStream->RcvUrgentMark();
itsStream->HandleDataArrived(itsParamBlock.csParam.receive.rcvBuff,
itsParamBlock.csParam.receive.rcvBuffLen,
itsStream->RcvUrgentStatus());
break;
case TCPClose:
itsStream->HandleClosed();
break;
case TCPRcvBfrReturn:
// if auto-receiving, re-issue the TCPNoCopyRcv command with same paramters
if ((itsParamBlock.csParam.open.options[36]) && (itsStream->hasSessionOpen)) {
itsParamBlock.csParam.receive.rdsLength = itsParamBlock.csParam.open.options[36];
itsParamBlock.ioCompletion = (TCPIOCompletionProc) &CompletionProc;
itsParamBlock.csCode = TCPNoCopyRcv;
PBControlAsync((ParmBlkPtr) &itsParamBlock);
return (FALSE); // make sure call object stays around
}
else
ForgetPtr(itsParamBlock.csParam.receive.rdsPtr);
break;
} // switch
} // if…else
return (TRUE); // always dispose of call object when done
}
/*______________________________________________________________________
**
** DispatchNoCopyRcv (protected method)
**
** Optimized routine to handle completion of TCPNoCopyRcv calls.
**
** return (Boolean): TRUE to dispose of call object when done
**
*/
Boolean CTCPAsyncCall::DispatchNoCopyRcv (void)
{
rdsEntry *RDSPtr;
// respond to error condition
if (itsParamBlock.ioResult) {
if ((itsParamBlock.ioResult != connectionClosing) && (itsParamBlock.ioResult != connectionDoesntExist))
itsStream->HandleTCPError(itsParamBlock.ioResult, itsParamBlock.csCode);
ForgetPtr(itsParamBlock.csParam.receive.rdsPtr);
return (TRUE); // always dispose when call fails
}
// update urgent flags
if (itsParamBlock.csParam.receive.urgentFlag)
itsStream->RcvUrgentBegin();
if (itsParamBlock.csParam.receive.markFlag)
itsStream->RcvUrgentMark();
// run through RDS and process each block of data
RDSPtr = (rdsEntry *) itsParamBlock.csParam.receive.rdsPtr;
while ((*RDSPtr).length) {
if (bypassProc)
(bypassProc) (bypassTarget, (*RDSPtr).ptr, (*RDSPtr).length, itsStream->rcvUrgent);
else
itsStream->HandleDataArrived((*RDSPtr).ptr,
(*RDSPtr).length, itsStream->rcvUrgent);
RDSPtr++;
}
// return the buffers to MacTCP
itsParamBlock.csCode = TCPRcvBfrReturn;
PBControlAsync((ParmBlkPtr) &itsParamBlock);
// if auto-receiving, the call will be reissued when the BfrReturn call is completed
return (FALSE); // keep call object around
}
// —— bypass procedure linkage ——
/*______________________________________________________________________
**
** SetRcvBypassProc
**
** Install a receive bypass procedure.
**
** aRcvBypassTarget (CObject *): the object to pass to the bypass procedure
** aRcvBypassProc (RcvBypassProc): the receive bypass procedure
**
*/
void CTCPAsyncCall::SetRcvBypassProc (CObject *aRcvBypassTarget,
RcvBypassProc aRcvBypassProc)
{
bypassTarget = aRcvBypassTarget;
bypassProc = aRcvBypassProc;
}
/***********************************************************************
************************************************************************
**
** INTERRUPT-LEVEL routine follows. This routine cannot make memory allocations, cannot make
** synchronous TCP calls, and cannot use the Think C profiler.
**
*/
#pragma options(!force_frame, !profile)
// inline assembly code used by CompletionProc
#pragma parameter __A0 GetTheCall ()
CTCPAsyncCall *GetTheCall (void) = {0x5988, 0x2050}; // SUBQ.L #4,A0
// // MOVE.L (A0),A0
// remember that theCall->qEntry.qSelfLink is immediately before itsParamBlock
// —— completion procedure ——
/*______________________________________________________________________
**
** CompletionProc (private static method)
**
** The asynchronous completion routine. Recieves notification that any asynchronous
** TCP I/O operation has been completed. This routine is used for all TCP calls placed by
** CTCPAsyncCall::DoAsyncCall.
**
** Send notification to the TCP driver object that this call has been completed. The driver
** then queues the call for processing at the next event loop.
**
** register A0 (struct TCPiobp *): the TCP I/O parameter block
**
*/
pascal void CTCPAsyncCall::CompletionProc (void)
{
register CTCPAsyncCall *theCall = GetTheCall();
theCall->qEntry.qType = asyncCall;
Enqueue((QElemPtr) &(theCall->qEntry), &(gTCPDriver->asyncQueue));
// CTCPDriver::ProcessNetEvents will pick this up later
}